#!/usr/bin/python
# -*- coding: utf-8 -*-

#Copyright (C) 2011 Daniele Simonetti
#
#This program is free software; you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation; either version 2 of the License, or
#(at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

import sys, locale, string, socket, time, re, os
### PySide ###
import PySide
### UI ###
from epescore.main_ui import *
### WIDGETS ###
from epescore import watchlist
from epescore import showinfo
from epescore import thisweek
### CONTENT PROVIDERS ###
from epescore import providers
### QUEUE ###
from Queue import Queue
### SQLITE ###
import sqlite3
### OS UTILS ###
from epescore import osutil
### TVRAGE ###
import tvrage.api
### GETTEXT ###
import gettext
### TO GET USERNAME ###
import getpass

### OPTIONAL UBUNTUONE SUPPORT ###
try:
    from epescore.uone import UbuntuOneBusiness
    HAVE_UBUNTU_ONE = True
    print 'Yeee I got UbuntuOne'
except Exception as e:
    print 'Boo no UbuntuOne'
    print e
    HAVE_UBUNTU_ONE = False

from epescore.evars import *

app = None

########## EPES CORE CLASS ############
class EpesCore(QtGui.QWidget, Ui_MyEpesForm):
  def __init__(self, db, parent=None):
    super(EpesCore, self).__init__(parent)
    self.setupUi(self)
    self.setWindowTitle(APP_NAME + ' v' + APP_VERSION)
    
    # SELECT SYSTEM FONT  
    if os.name == 'nt':
        # Default font on Windows sucks...
        app_font = QtGui.QFont('Century Gothic', 10)
    else:
        app_font = osutil.get_system_font()
    self.setFont(app_font)           
    
    # PAGES
    self.watchlist = watchlist.Watchlist(self)
    self.showinfo = showinfo.ShowInfo(self.watchlist, self)
    self.thisweek = thisweek.ThisWeek(self.watchlist, self)
    
    self.watchlist.setFont(app_font)
    self.showinfo.setFont(app_font)
    self.thisweek.setFont(app_font)
    
    self.tabs.setDocumentMode(True)
    self.tabs.insertTab(0, self.watchlist,  _("Watchlist"))
    self.tabs.insertTab(1, self.showinfo,   _("Show Informations"))
    self.tabs.insertTab(2, self.thisweek,   _("This week"))
        
    # QUEUE AND CONTENT PROVIDERS
    #self.queue = Queue()
    self.running_providers = []
    
    # CACHE
    self.show_cache = {}
    
    # BUSINESS
    self.db = db

    # SIGNALS/SLOTS
    self.watchlist.searchSerie.connect(self.search_serie)
    self.watchlist.needSerieInfo.connect(self.serie_eps)
    self.watchlist.needBatchSerieInfo.connect(self.serie_batch_eps)
    self.watchlist.removedSerie.connect(self.serie_remove)
    self.watchlist.rageSerie.connect(self.serie_rage)

    self.showinfo.needShowInfo.connect(self.serie_info)
    self.showinfo.needShowEpList.connect(self.serie_eps)
    
    self.thisweek.needShowEpList.connect(self.serie_eps)
    self.thisweek.needBatchSerieInfo.connect(self.serie_batch_eps)
    
    self.enable_uone.toggled.connect(self.uone_toggled)

    # LOAD SETTINGS
    settings = QtCore.QSettings()
    geo = settings.value('geometry')
    if geo is not None:
        self.restoreGeometry(geo)
    else:
        self.setGeometry(QtCore.QRect(100,100,680,800))
        
    # SET THE USERNAME
    self.lb_username.setText(getpass.getuser())
        
    # APPLY GLOBAL STYLE SHEET        
    self.setStyleSheet('''QLineEdit {
    border: 1px solid lightgray;
    border-radius: 8px;
}

QComboBox {
    border: 1px solid lightgray;
    border-radius: 8px;
}

QComboBox::drop-down { 
    image: url(:/icons/down_arrow_cb.png);
}
    ''')

  def event(self, e):
    if e.type() == providers.showsEventType:
      for s in e.shows:
        self.show_cache[s.showid] = s
      return True
    elif e.type() == providers.epsEventType:
      #print 'questo evento non dovrebbe arrivare a me'
      pass
    return super(EpesCore, self).event(e)
    
  def closeEvent(self, e):  
    # Hide right away and let the threads end
    self.hide()
    
    for p in self.running_providers:
        if p.is_alive():
            p.set_closing()
            p.join(100)
    
    i = 0
    for item in self.watchlist.get_items():
        if self.db.has_show(item):
            self.db.update_show(item, i)
        else:
            self.db.insert_show(item, i)
        i += 1
    self.db.commit_and_close()
    settings = QtCore.QSettings()
    settings.setValue('geometry', self.saveGeometry())
    
    print 'sono arrivato qui... quanti ancora devono finire?'
  
  def load(self):
    all_items = self.db.fetch_all_items()
    
    # SET THE COUNT ON THE HEADER
    msg = _('Following %d TV Shows') % len(all_items)
    self.lb_showcount.setText(msg)
    
    self.watchlist.add_batch_items(all_items)
    self.watchlist.setupModel()
    self.thisweek.update()
   
  def serie_eps(self, tosearch):    
    item = {}
    item['showid'] = tosearch
    #print 'search eps for: %s' % (item)
    queue = Queue()
    queue.put(item, True)
    self.query_provider('eps', queue, self.sender())
    
  def serie_batch_eps(self, tosearch):
    queue = Queue()
    for id in tosearch:
        item = {}
        item['showid'] = id
        queue.put(item, True) 
    print 'batch request from %s' % str(self.sender())
    self.query_provider('eps', queue, self.sender())
    
  def serie_info(self, tosearch):    
    item = {}
    item['showid'] = tosearch
    #print 'search eps for: %s' % (item)
    queue = Queue()
    queue.put(item, True)
    self.query_provider('showinfo', queue, self.sender())    
    
  def search_serie(self, tosearch):
    #print 'search serie: %s -> %s' % (tosearch, str(self.sender()))
    item = {}
    item['show'] = unicode(tosearch)
    queue = Queue()
    queue.put(item, True)
    self.query_provider('show', queue, self.sender())
  
  def serie_remove(self, removed):
    self.db.remove_show(removed)
  
  def query_provider(self, what, queue, who = None):
    # TODO: Select from multiple providers  
    if who is None:
        who = self
        
    provider = None
    
    if what == 'show':
        provider = providers.TvRageShowProvider(queue, who)
    elif what == 'eps':
        provider = providers.TvRageEpisodeProvider(queue, who)
    elif what == 'showinfo':
        provider = providers.TvRageShowInfoProvider(queue, who)
    if provider is not None:
        provider.start()
        
    self.running_providers.append(provider)
  
  def serie_rage(self, showid):
    print 'open in tv rage ' + showid 
    queue = Queue()
    if showid not in self.show_cache:
        queue.put({'showid' : showid, 'method' : 'send'}, True)
        self.query_provider('showinfo', queue, self)
        queue.join()
    osutil.portable_open(self.show_cache[showid].link)
    
  def do_uone_sync(self):
    uonedb = UbuntuOneBusiness()
    if not uonedb.create_db():
        return False

    # STEP 1: RETRIEVE SHOWS FROM UONE
    fetched = uonedb.fetch_all()   
    print 'fetched %d items from cloud' % len(fetched)
    
    # STEP 2: SAVE TO LOCAL DB
    for item in fetched:
        print item
        if self.db.has_show(item):
            self.db.update_show(item, item['order'])
        else:
            self.db.insert_show(item, item['order'])
    # STEP 3: DUMP WHOLE DB TO CLOUD        
    fetched = self.db.fetch_all_items()    
    i = 0
    for r in fetched:
        # item[0], item[1], item[2], item[3]
        item = { 'showid' : r[0], 'title': r[1], 'lastep': r[2], 'rating': r[3] }
        uonedb.update_show(item, i)
        i += 1
    # STEP 4: UPDATE UI
    self.load()
    
  def uone_toggled(self, flag):
      if flag == True and HAVE_UBUNTU_ONE:
        self.do_uone_sync()
        

################## DB BUSINESS CLASS ##################

class EpesBusiness:
    def __init__(self):
        self.dbfile = osutil.get_user_data_path('epes.db')
        self.dbconn = None
        
    def check_db(self):
        return self.connect_db() and self.create_db()
        
    def connect_db(self):
        self.dbconn = sqlite3.connect(self.dbfile)
        return self.dbconn is not None
      
    def commit_and_close(self):
        self.dbconn.commit()
        self.dbconn.close()
    
    def create_db(self):
        try:
            # create shows table
            c = self.dbconn.cursor()
            c.execute('''create table shows
            (showid varchar primarykey, title varchar, last_see_ep integer, sortorder integer, rating integer)''')
            
            # create eps table
            #c.execute('''create table eps
            #(showid varchar primarykey, season integer, epnum integer primarykey, seasonnum integer, eptitle text, airdate datetime,
            #eplink text)''')
            
            # save (commit) and close the cursor
            self.dbconn.commit()
            c.close()
        except:
            print 'probably tables already exist... check if we need to update db'
            if self.need_to_update():
                self.update_db()
        return True
           
    def insert_show(self, item, order = -1):
        c = None
        try:
            t = ( str(item['showid']), unicode(item['title']), int(item['lastep']), order, int(item['rating']) )
            c = self.dbconn.cursor()
            c.execute('''insert into shows
            values (?,?,?,?,?)''', t)
        except Exception as e:
            print 'insert show failed'
            print e
            return False
        finally:
            c.close()
        return True
        #for ep in item['eps']:
        #    try:
        #        t = ( item['showid'], int(ep.season), int(ep.ep), int(ep.seasonnum),
        #        ep.title, ep.airdate)
        #        c.execute('''insert into eps
        #        (?, ?, ?, ?, ?, ?, ?)''', 
        #    except:
        #        print 'insert episode failed'
    
    def update_show(self, item, order = -1):
        c = None
        if order >= 0:
            print '%s at order %d' % (item['showid'], order)
        try:
            t = ( unicode(item['title']), int(item['lastep']), order, int(item['rating']), str(item['showid']) )
            c = self.dbconn.cursor()
            c.execute('''update shows set title=?, last_see_ep=?, sortorder=?, rating=? where showid=?''', t)
        except Exception as e:
            print 'update show failed'
            print e
            return False
        finally:
            c.close()
        return True        
    
    def has_show(self, item):
        result = False
        try:
            t = ( str(item['showid']), )
            c = self.dbconn.cursor()
            c.execute('''select showid from shows where showid=?''', t)
            result = c.fetchone() is not None
        except Exception as e:
            print 'has item failed'
            print e
        finally:
            c.close()
        return result
            
    def remove_show(self, id):
        result = True
        try:
            t = (id,)
            c = self.dbconn.cursor()
            c.execute('''delete from shows where showid=?''', t)
        except Exception as e:
            print 'delete item failed'
            print e
            result = False
        finally:
            c.close()
        return result
    
    def fetch_all_items(self, ordered = True):
        try:
            c = self.dbconn.cursor()
            if ordered:
                c.execute('''select showid,title,last_see_ep,rating from shows order by sortorder''')
            else:
                c.execute('''select showid,title,last_see_ep,rating from shows''')
            return c.fetchall()
        except Exception as e:
            print 'fetch all failed'
            print e
        finally:
            c.close()
        return None
        
    def need_to_update(self):
        try:
            c = self.dbconn.cursor()
            c.execute('''select sortorder, rating from shows''')
            c.fetchone()
        except Exception as e:            
            print 'this db needs to be updated'
            return True
        finally:
            c.close()
        print 'this db is already updated to the last version'
        return False 
        
    def update_db(self):
        try:
            # create shows table
            c = self.dbconn.cursor()
            c.execute('''alter table shows add sortorder integer''')

            c.execute('''alter table shows add rating integer''')
            
            # save (commit) and close the cursor
            self.dbconn.commit()
            c.close()
        except Exception as e:
            print e
            print 'db update failed... please delete the db and try again'
        
################## MAIN ##################
if __name__ == "__main__":
  app = QtGui.QApplication(sys.argv)
  locale.setlocale(locale.LC_ALL,"")
  
  machine_locale = locale.getdefaultlocale()[0]
  locale_dir = '/usr/share/locale'
  if not os.path.exists(locale_dir):
      locale_dir = os.path.join(sys.path[0], 'locale')
  t = gettext.translation('epes', locale_dir, [machine_locale])
  _ = t.gettext

    
  QtCore.QCoreApplication.setApplicationName(APP_NAME)
  QtCore.QCoreApplication.setApplicationVersion(APP_VERSION)
  QtCore.QCoreApplication.setOrganizationName(APP_ORG)
  
  # be sure that user_data_path exists
  user_data_path = osutil.get_user_data_path()
  if not os.path.exists(user_data_path):
    os.makedirs(user_data_path)
  
  image_cache_dir = osutil.get_user_data_path('image_cache')
  if not os.path.exists(image_cache_dir):
    os.mkdir(image_cache_dir)
      
  epesDb = EpesBusiness()
  if not epesDb.check_db():
    print 'could not open or create db'
    sys.exit(1)
  epesform = EpesCore(epesDb)   
  epesform.load()
  epesform.show()
  sys.exit(app.exec_())
